﻿using MTP.Buffer;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace MTP.Utils
{
    class Util
    {

        private static DateTime LastCollectDate = DateTime.Now;

        public static readonly TaskFactory TaskFactory = new TaskFactory(TaskCreationOptions.PreferFairness, TaskContinuationOptions.AttachedToParent);

        public static readonly TaskFactory LongFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);
        /// <summary>
        /// 定时回收垃圾
        /// </summary>
        public static void Collect()
        {
            if ((DateTime.Now - LastCollectDate).TotalMinutes > 2)
            {
                GC.Collect();
            }
        }

        public static Socket GetSocket(AddressFamily addressFamily)
        {
            return new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
        }

        private static DateTime ToLocalTime(DateTime dt)
        {
            return TimeZoneInfo.ConvertTime(dt, TimeZoneInfo.Utc, TimeZoneInfo.Local).ToLocalTime();
        }

        /// <summary>
        /// 当前时间秒戳
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static long CurrentTimestamp()
        {
            DateTime dateTime = DateTime.Now;
            DateTime startTime = ToLocalTime(new DateTime(1970, 1, 1));
            return (long)(dateTime - startTime).TotalSeconds;
        }

        /// <summary>
        /// 创建握手包
        /// </summary>
        /// <param name="key">密钥</param>
        /// <returns></returns>
        public static ByteWriteBuffer Handshake(byte[] cipher, Guid guid)
        {
            var buffer = ByteWriteBuffer.Alloc(MTPConst.HANDSHAKE_LENGTH);
            buffer.Write(MTPConst.PROTOCOL_HEADER);
            buffer.Write(guid.ToByteArray());
            buffer.Write(CurrentTimestamp(), 5);
            buffer.Write(MD5Sign(buffer.ToBytes(), cipher));
            return buffer;
        }

        /// <summary>
        /// 验证握手包
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public static bool VerifyHandshake(Func<byte[], int> receive, byte[] cipher, out Guid guid)
        {
            var bytes = new byte[MTPConst.HANDSHAKE_LENGTH];
            int length = receive(bytes);
            if (length != MTPConst.HANDSHAKE_LENGTH)
                return false;
            // 验证握手包
            if (!MTPConst.PROTOCOL_HEADER.SequenceEqual(bytes.Take(3)))
                return false;
            guid = new Guid(bytes.Skip(3).Take(16).ToArray());
            var md5 = bytes.Skip(24);
            var timestamp = bytes.CopyToNew(19, 5).ToInt64();
            // 验证时间戳
            if (Math.Abs(timestamp - CurrentTimestamp()) > 5)
                return false;
            return MD5Sign(bytes.Take(24).ToArray(), cipher).SequenceEqual(md5);
        }



        /// <summary>
        /// 创建包
        /// </summary>
        /// <param name="guid"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static ByteWriteBuffer BuilderPackage(Guid guid, int index, bool isEnd, byte[] bytes)
        {
            int sequence = index << 1;
            if (isEnd)
                sequence += 1;
            var buffer = ByteWriteBuffer.Alloc(MTPConst.HEADER_LENGTH + bytes.Length);
            buffer.Write(guid.ToByteArray());
            buffer.Write(sequence, 2);
            buffer.Write(bytes.Length, 2);
            buffer.Write(0, 3);
            buffer.Write(bytes);
            return buffer;
        }

        /// <summary>
        /// 首包
        /// </summary>
        /// <param name="guid"></param>
        /// <param name="index"></param>
        /// <param name="isEnd"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static ByteWriteBuffer BuilderFirestPackage(Guid guid, PackageType packageType, IDictionary<string, string> header, bool isEnd, byte[] bytes)
        {
            var headerBytes = ToQueryKeyValue(header);
            var buffer = ByteWriteBuffer.Alloc(MTPConst.HEADER_LENGTH + headerBytes.Length + bytes.Length);
            buffer.Write(guid.ToByteArray());
            buffer.Write(isEnd ? 1 : 0, 2);
            buffer.Write(bytes.Length, 2);
            buffer.Write((int)packageType, 1);
            buffer.Write(headerBytes.Length, 2);
            if (headerBytes.Length > 0)
                buffer.Write(headerBytes);
            buffer.Write(bytes);
            return buffer;
        }
        /// <summary>
        /// MD5签名
        /// </summary>
        /// <param name="data"></param>
        /// <param name="keyBytes"></param>
        /// <returns></returns>
        public static byte[] MD5Sign(byte[] data, byte[] cipher)
        {
            var sign = new byte[data.Length + cipher.Length];
            Array.Copy(data, sign, data.Length);
            Array.Copy(cipher, 0, sign, data.Length, cipher.Length);
            using (MD5 md5 = MD5.Create())
            {
                return md5.ComputeHash(sign);
            }
        }

        public static byte[] ToQueryKeyValue(IEnumerable<KeyValuePair<string, string>> dictionary)
        {
            if (dictionary == null || dictionary.Count() == 0)
            {
                return new byte[0];
            }
            StringBuilder sb = new StringBuilder();
            foreach (var kv in dictionary)
            {
                if (kv.Value != null)
                {
                    sb.Append($"{kv.Key}={UrlEncode(kv.Value)}&");
                }
            }
            return Encoding.UTF8.GetBytes(sb.ToString().TrimEnd('&'));
        }

        /// <summary>
        /// 键值对转 字典
        /// </summary>
        /// <param name="query"></param>
        /// <param name="encode"></param>
        /// <returns></returns>
        public static Dictionary<string, string> QueryToDictionary(byte[] bytes)
        {
            if (bytes.Length == 0 || bytes == null)
            {
                return new Dictionary<string, string>();
            }
            string query = Encoding.UTF8.GetString(bytes);
            StringBuilder sb = new StringBuilder();
            return query.Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries)
                  .Select(a => a.Split('='))
                  .ToDictionary(a => a[0], a => a.Length > 1 ? (UrlDecode(a[1])) : "", StringComparer.OrdinalIgnoreCase);
        }

        public static string UrlEncode(string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return str;
            }
            return HttpUtility.UrlEncode(str, Encoding.UTF8);
        }

        /// <summary>
        /// UrlDecode Url解码
        /// </summary>
        /// <param name="str">待解码字符串</param>
        /// <param name="encoding">解码规则</param>
        /// <returns>解码后的字符串</returns>
        public static string UrlDecode(string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return str;
            }
            return HttpUtility.UrlDecode(str, Encoding.UTF8);
        }
    }
}
